////////////////////////////////////////////////////////////////////////////////

#ifndef _SERVERNETWORK_H_
#include "servernetwork.h"
#endif

////////////////////////////////////////////////////////////////////////////////

#ifndef _NETWORKPACKET_H_
#include "networkpacket.h"
#endif

#ifndef _SERVER_H_
#include "server.h"
#endif

#ifndef _SAVER_H_
#include "saver.h"
#endif

#ifndef _LOADER_H_
#include "loader.h"
#endif

#ifndef _BOOSTSERVICE_H_
#include "boostservice.h"
#endif

#ifndef _SCOPEGUARD_H_
#include "scopeguard.h"
#endif

#include <cassert>
#include <iostream>
#include <boost/bind.hpp>

////////////////////////////////////////////////////////////////////////////////


ServerNetwork::ServerNetwork()
: m_Acceptor( NetworkFactory::GetAcceptor(27027) )
{
    // Setup observer
    AddEventToObserve( Observer::NETWORK_RECEIVE_COMPLETE_TCP );
    AddEventToObserve( Observer::NETWORK_RECEIVE_COMPLETE_UDP );
    AddEventToObserve( Observer::NETWORK_SEND_COMPLETE_TCP );
    AddEventToObserve( Observer::NETWORK_ACCEPTED_NEW_CONNECTION );
    AddEventToObserve( Observer::NETWORK_DISCONNECT );

    // Setup observer
    m_Acceptor->AddObserver(this);

    // Accept first client
    Accept();

    BoostService::Run();
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::Accept()
{
    // Accept new client
    m_Acceptor->Start();
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::HandleReceiveTCP( const IServerConnection& _client )
{
    // Read received data into buffer
    Buffer buffer( _client.GetTCP().Data(),
                   _client.GetTCP().Size() );

    // Compose a client packet from it
    ProcessPacketInBuffer(_client.GetID(), buffer);
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::HandleReceiveUDP( const IServerConnection& _client )
{
    // Read received data into buffer
    Buffer buffer( _client.GetUDP().Data(),
                   _client.GetUDP().Size() );

    // Compose a client packet from it
    ProcessPacketInBuffer(_client.GetID(), buffer);
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::SendServerState( int _clientID, const ServerStatePacket& _serverStatePacket )
{
    SendUDP(_clientID, _serverStatePacket);
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::SendServerInfo( int _clientID, const Packet& _serverPacket )
{
    SendTCP(_clientID, _serverPacket);
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::SendTCP( int _clientID, const Packet& _packet )
{
    // Save packet into buffer
    Saver saver;
    _packet.AcceptSaver(saver);

    // Compose a network packet
    NetworkPacket networkPacket( saver.GetBuffer().Data(),
                                 saver.GetBuffer().Size() );

    // Send it to client
    IServerConnection& connection = FindConnectionByID(_clientID);
    connection.SendTCP(networkPacket);
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::SendUDP( int _clientID, const Packet& _packet )
{
    // Save packet into buffer
    Saver saver;
    _packet.AcceptSaver(saver);

    // Compose a network packet
    NetworkPacket networkPacket( saver.GetBuffer().Data(),
                                 saver.GetBuffer().Size() );

    // Send it to client
    IServerConnection& connection = FindConnectionByID(_clientID);
    connection.SendUDP(networkPacket);
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::OnEvent( Event _event, Observable* _observable )
{
    switch (_event)
    {
    case Observer::NETWORK_RECEIVE_COMPLETE_TCP:
        HandleReceiveTCP( *dynamic_cast<IServerConnection*>(_observable) );
        break;

    case Observer::NETWORK_RECEIVE_COMPLETE_UDP:
        HandleReceiveUDP( *dynamic_cast<IServerConnection*>(_observable) );
        break;

    case Observer::NETWORK_ACCEPTED_NEW_CONNECTION:
        HandleAccept();
        break;

    case Observer::NETWORK_DISCONNECT:
        HandleDisconnect( *dynamic_cast<IServerConnection*>(_observable) );
        break;

    default:
        break;
    }
}


////////////////////////////////////////////////////////////////////////////////


IServerConnection& ServerNetwork::FindConnectionByID( int _ID )
{
    // Prevent other threads from access to ConnectionsByIDs collection
    ScopeGuard guard(m_ConnectionsByIDs);

    ConnectionsByIDs::iterator it = m_ConnectionsByIDs.find(_ID);
    if ( it == m_ConnectionsByIDs.end() )
        throw "Connection not found";

    return *(it->second);
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::HandleAccept()
{
    // Generate ID for new connection
    static int ID = 0;
    ++ID;

    // Create new connection
    ServerConnectionPtr newConnection( NetworkFactory::GetServerConnection(ID, m_Acceptor) );

    // Setup it
    newConnection->AddObserver(this);

    {
        // Prevent other threads from access to ConnectionsByIDs collection
        ScopeGuard guard(m_ConnectionsByIDs);

        // Add it to collection
        m_ConnectionsByIDs.insert( std::make_pair(ID, newConnection) );
    }


    // Notify client about his ID
    ClientIDPacket packetWithIDForClient(ID);
    SendTCP(ID, packetWithIDForClient);

    // Start accept new connections
    Accept();
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::HandleDisconnect( const IServerConnection& _client )
{
    // Disconnect client in engine
    Server::Instance().Engine().RemoveClient( _client.GetID() );

    // Mark this connection for deletion
    MarkConnectionForDeletion( _client.GetID() );
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::DeleteConnection( int _ID )
{
    // Prevent other threads from access to ConnectionsByIDs collection
    ScopeGuard guard(m_ConnectionsByIDs);

    // Delete connection
    m_ConnectionsByIDs.erase(_ID);
    std::cout << "connection #"<< _ID << " deleted" << std::endl;
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::ProcessPacketInBuffer( int _clientID, Buffer& _buffer )
{
    Packet::PacketCode code;
    _buffer.ReadMemory( &code, sizeof(Packet::PacketCode) );

    Loader loader(_buffer);

    switch (code)
    {
    case Packet::CLIENT_INPUT:
        {
            ClientInputPacket clientInputPacket = loader.LoadClientInputPacket();
            Server::Instance().Engine().UpdateClientInput(_clientID, clientInputPacket);
            break;
        }

    case Packet::CLIENT_INFO:
        {
            ClientInfoPacket clientInfoPacket = loader.LoadClientInfoPacket();
            Server::Instance().Engine().UpdateClientInfo(_clientID, clientInfoPacket);
            break;
        }

    default:
        throw "not implemented";
    }
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::MarkConnectionForDeletion( int _clientID )
{
    m_ConnectionsToDelete.push_back(_clientID);
}


////////////////////////////////////////////////////////////////////////////////


void ServerNetwork::DeleteObsoleteConnections()
{
    if ( m_ConnectionsToDelete.empty() )
        return;

    for (IntVec::iterator it = m_ConnectionsToDelete.begin();
         it != m_ConnectionsToDelete.end(); ++it)
         DeleteConnection(*it);

    m_ConnectionsToDelete.clear();
}


////////////////////////////////////////////////////////////////////////////////
